/*
 * @filename StringUtils.java
 * 
 * @author barry
 * 
 * @version 0.0.1
 * 
 * @date 2020年3月4日
 */
package com.bnzj.core.persistence.query.conversion;

/**
 * @author barry
 * @date 2020-03-04
 */
public class ConversionTool {
    
    private static final char CASE_MASK = 0x20;

    public static char toUpperCase(char c) {
      return isLowerCase(c) ? (char) (c ^ CASE_MASK) : c;
    }

    public static boolean isLowerCase(char c) {
        // Note: This was benchmarked against the alternate expression "(char)(c - 'a') < 26" (Nov '13)
        // and found to perform at least as well, or better.
        return (c >= 'a') && (c <= 'z');
    }

    public static boolean isUpperCase(char c) {
        return (c >= 'A') && (c <= 'Z');
    }

    /**
     * Returns a copy of the input string in which all {@linkplain #isLowerCase(char) lowercase ASCII
     * characters} have been converted to uppercase. All other characters are copied without
     * modification.
     */
    public static String toUpperCase(String string) {
      int length = string.length();
      for (int i = 0; i < length; i++) {
        if (isLowerCase(string.charAt(i))) {
          char[] chars = string.toCharArray();
          for (; i < length; i++) {
            char c = chars[i];
            if (isLowerCase(c)) {
              chars[i] = (char) (c ^ CASE_MASK);
            }
          }
          return String.valueOf(chars);
        }
      }
      return string;
    }

    /**
     * Returns a copy of the input string in which all {@linkplain #isUpperCase(char) uppercase ASCII characters} have
     * been converted to lowercase. All other characters are copied without modification.
     */
    public static String toLowerCase(String string) {
        int length = string.length();
        for (int i = 0; i < length; i++) {
            if (isUpperCase(string.charAt(i))) {
                char[] chars = string.toCharArray();
                for (; i < length; i++) {
                    char c = chars[i];
                    if (isUpperCase(c)) {
                        chars[i] = (char)(c ^ CASE_MASK);
                    }
                }
                return String.valueOf(chars);
            }
        }
        return string;
    }
    
    public static void checkArgument(boolean expression) {
        if (!expression) {
            throw new IllegalArgumentException();
        }
    }
    
    public static void checkArgument(boolean b, String errorMessageTemplate, Object p1, Object p2, Object p3, Object p4) {
        if (!b) {
            throw new IllegalArgumentException(lenientFormat(errorMessageTemplate, p1, p2, p3, p4));
        }
    }

    public static <T extends Object> T checkNotNull(T reference) {
        if (reference == null) {
            throw new NullPointerException();
        }
        return reference;
    }

    public static <T extends Object> T checkNotNull(T reference, Object errorMessage) {
        if (reference == null) {
            throw new NullPointerException(String.valueOf(errorMessage));
        }
        return reference;
    }
    
    public static int checkPositionIndex(int index, int size) {
        return checkPositionIndex(index, size, "index");
    }

    public static int checkPositionIndex(int index, int size, String desc) {
        // Carefully optimized for execution by hotspot (explanatory comment above)
        if (index < 0 || index > size) {
            throw new IndexOutOfBoundsException(badPositionIndex(index, size, desc));
        }
        return index;
    }

    private static String badPositionIndex(int index, int size, String desc) {
      if (index < 0) {
        return lenientFormat("%s (%s) must not be negative", desc, index);
      } else if (size < 0) {
        throw new IllegalArgumentException("negative size: " + size);
      } else { // index > size
        return lenientFormat("%s (%s) must not be greater than size (%s)", desc, index, size);
      }
    }

    /**
     * Returns the given {@code template} string with each occurrence of {@code "%s"} replaced with the corresponding
     * argument value from {@code args}; or, if the placeholder and argument counts do not match, returns a best-effort
     * form of that string. Will not throw an exception under normal conditions.
     *
     * <p>
     * <b>Note:</b> For most string-formatting needs, use {@link String#format String.format},
     * {@link java.io.PrintWriter#format PrintWriter.format}, and related methods. These support the full range of
     * <a href="https://docs.oracle.com/javase/9/docs/api/java/util/Formatter.html#syntax">format specifiers</a>, and
     * alert you to usage errors by throwing {@link java.util.IllegalFormatException}.
     *
     * <p>
     * In certain cases, such as outputting debugging information or constructing a message to be used for another
     * unchecked exception, an exception during string formatting would serve little purpose except to supplant the real
     * information you were trying to provide. These are the cases this method is made for; it instead generates a
     * best-effort string with all supplied argument values present. This method is also useful in environments such as
     * GWT where {@code
     * String.format} is not available. As an example, method implementations of the {@link Preconditions} class use
     * this formatter, for both of the reasons just discussed.
     *
     * <p>
     * <b>Warning:</b> Only the exact two-character placeholder sequence {@code "%s"} is recognized.
     *
     * @param template
     *            a string containing zero or more {@code "%s"} placeholder sequences. {@code
     *     null} is treated as the four-character string {@code "null"}.
     * @param args
     *            the arguments to be substituted into the message template. The first argument specified is substituted
     *            for the first occurrence of {@code "%s"} in the template, and so forth. A {@code null} argument is
     *            converted to the four-character string {@code "null"}; non-null values are converted to strings using
     *            {@link Object#toString()}.
     * @since 25.1
     */
    // TODO(diamondm) consider using Arrays.toString() for array parameters
    public static String lenientFormat(String template, Object... args) {
        template = String.valueOf(template); // null -> "null"

        if (args == null) {
            args = new Object[] {"(Object[])null"};
        } else {
            for (int i = 0; i < args.length; i++) {
                args[i] = lenientToString(args[i]);
            }
        }

        // start substituting the arguments into the '%s' placeholders
        StringBuilder builder = new StringBuilder(template.length() + 16 * args.length);
        int templateStart = 0;
        int i = 0;
        while (i < args.length) {
            int placeholderStart = template.indexOf("%s", templateStart);
            if (placeholderStart == -1) {
                break;
            }
            builder.append(template, templateStart, placeholderStart);
            builder.append(args[i++]);
            templateStart = placeholderStart + 2;
        }
        builder.append(template, templateStart, template.length());

        // if we run out of placeholders, append the extra args in square braces
        if (i < args.length) {
            builder.append(" [");
            builder.append(args[i++]);
            while (i < args.length) {
                builder.append(", ");
                builder.append(args[i++]);
            }
            builder.append(']');
        }

        return builder.toString();
    }

    private static String lenientToString(Object o) {
        try {
            return String.valueOf(o);
        } catch (Exception e) {
            // Default toString() behavior - see Object.toString()
            String objectToString = o.getClass().getName() + '@' + Integer.toHexString(System.identityHashCode(o));
            // Logger is created inline with fixed name to avoid forcing Proguard to create another class.
            return "<" + objectToString + " threw " + e.getClass().getName() + ">";
        }
    }
}
